#include "vsl.h"

#include <cctype>
#include <iomanip>

#include <cstdlib>

VSL::VSL(std::streambuf *buf)
{
    this->codebuf << buf;
//    this->codebuf >> std::noskipws;
//    std::string str;
//    char ch;
//    while (this->codebuf >> ch) {
//        std::cout << "[" << ch << "]"<< std::endl;
//    }
    std::cout << this->codebuf.str() << std::endl;
    std::cout << "................................................" << std::endl;
}

void VSL::dumptoks()
{

    //std::cout.setf(std::ios::left);
    while ((!this->codebuf.eof()) && this->tok.type != TOK_EOF) {
        this->gettok();
        std::string t = "<" + tokstr[this->tok.type] + ">";
        std::cout << std::left << std::setw(16) << t << "\t" << this->tok.text << std::endl;
    }
    // restore
    this->codebuf.clear();
    this->codebuf.seekg(0);
    //this->codebuf.seekp(0);
}

TokenType VSL::gettok()
{
    if (this->codebuf.peek() == 255) {
        std::cerr << "Token End..." << std::endl;
    }
    this->tok.type = TOK_UNKNOWN;
    this->tok.text = "";
    // whitespace
    while (isspace(this->codebuf.peek())) {
        this->codebuf.get();
    }

    unsigned char ch = this->codebuf.get();
    if (ch == '\"') {
        this->tok.type = TOK_STRING;
        //this->tok.text = ch;
        while ((ch = this->codebuf.get()) != '\"') {
            this->tok.text += ch;
        }
        // TODO: ESCAPE String
        //this->tok.text += ch;
        return this->tok.type;
    }

    if (ispunct(ch)) {
        //std::cout << "@:" << ch << std::endl;
        if (ch == '+' || ch == '-' || ch == '*') {
            this->tok.type = TOK_BINARY;
            this->tok.text = ch;
            return this->tok.type;
        }
        if (ch == '/') {
            if (this->codebuf.peek() == '/') {  // commnet
                this->codebuf.unget();
                this->tok.type = TOK_COMMENT;
                std::getline(this->codebuf, this->tok.text);
                //this->tok.text = this->codebuf.getline()
            } else {
                this->tok.type = TOK_BINARY;
                this->tok.text = ch;
            }
            return this->tok.type;
        }
        if (ch == ':') {
            if (this->codebuf.peek() == '=') {
                this->tok.type = TOK_ASSIGN_SYMBOL;
                //std::cout << "#:" << ch << std::endl;
                this->tok.text = ch;
                this->tok.text += this->codebuf.get();
                //std::cout << "#:" << this->tok.text << std::endl;
                return this->tok.type;
            }
        }

        if (ch == '(') {
            this->tok.type = TOK_LPAREN;
            this->tok.text = ch;
            return this->tok.type;
        }
        if (ch == ')') {
            this->tok.type = TOK_RPAREN;
            this->tok.text = ch;
            return this->tok.type;
        }
        if (ch == '{') {
            this->tok.type = TOK_LCURLY;
            this->tok.text = ch;
            return this->tok.type;
        }
        if (ch == '}') {
            this->tok.type = TOK_RCURLY;
            this->tok.text = ch;
            return this->tok.type;
        }
        if (ch == ',') {
            this->tok.type = TOK_COMMA;
            this->tok.text = ch;
            return this->tok.type;
        }
        this->tok.type = TOK_UNKNOWN;
        this->tok.text = ch;
        return this->tok.type;
    }

    if (isalpha(ch)) {
        do {
          this->tok.text += ch;
        } while (isalnum(ch = this->codebuf.get()));
        // current ch is next char
        this->codebuf.unget();
        if (this->tok.text == "FUNC") {
            this->tok.type = TOK_FUNC;
        } else if (this->tok.text == "PRINT") {
            this->tok.type = TOK_PRINT;
        } else if (this->tok.text == "RETURN") {
            this->tok.type = TOK_RETURN;
        } else if (this->tok.text == "CONTINUE") {
            this->tok.type = TOK_CONTINUE;
        } else if (this->tok.text == "IF") {
            this->tok.type = TOK_IF;
        } else if (this->tok.text == "ELSE") {
            this->tok.type = TOK_ELSE;
        } else if (this->tok.text == "FI") {
            this->tok.type = TOK_FI;
        } else if (this->tok.text == "WHILE") {
            this->tok.type = TOK_WHILE;
        } else if (this->tok.text == "DO") {
            this->tok.type = TOK_DO;
        } else if (this->tok.text == "DONE") {
            this->tok.type = TOK_DONE;
        } else if (this->tok.text == "VAR") {
            this->tok.type = TOK_VAR;
        } else {
            this->tok.type = TOK_VARIABLE;
        }
        return this->tok.type;
    }

    if (isdigit(ch)) {
        do {
            this->tok.text += ch;
        } while (isdigit(ch = this->codebuf.get()));
        this->codebuf.unget();
        this->tok.type = TOK_NUMBER;
        return this->tok.type;
    }

    if (ch == 255) {
        this->tok.type = TOK_EOF;
        this->tok.text = "EOF";
    }
    //std::cout << "&:" << int(ch) << std::endl;
    return this->tok.type;
}

TokenType VSL::getk()
{
    this->gettok();
    this->printtok();
    return this->tok.type;
}

TokenType VSL::getTokType()
{
    return this->tok.type;
}

std::string VSL::getTokStr()
{
    return this->tok.text;
}


void VSL::printtok()
{
    std::string t = "<" + tokstr[this->tok.type] + ">";
    std::clog << "[DEBUG]:" << std::left << std::setw(16) << t << "  " << this->tok.text << std::endl;
}

void VSL::parser()
{
    std::cout << "\n\n===-----Parser-----===\n\n" << std::endl;
    BaseAST *p = this->programParser();

    std::cout << "\n\n===-----End-----===\n\n" << std::endl;
}

BaseAST *VSL::programParser()
{
    FuncListAST *program;
    this->gettok();
    this->printtok();
    while (this->getTokType() != TOK_EOF) {
        this->gettok();
        //this->printtok();
        switch (this->getTokType()) {
        case TOK_COMMENT:
            break;  // pass comment
        case TOK_FUNC:
            program->add(this->funcParser());
            break;
        default:
            std::cerr << "programParser Error" << std::endl;
            return NULL;
        }
    }
    return program;
}

FuncAST *VSL::funcParser()
{
    std::clog << "Test...." << std::endl;
    FuncDeclAST *decl = this->funcDeclParser();
    exit(1);
    //BaseAST *body = blockStmtParser();
}

FuncDeclAST *VSL::funcDeclParser()
{
    IdentAST *id;
    std::vector<IdentAST *> args;
    if (this->getk() == TOK_VARIABLE) {
        id = new IdentAST(this->tok.text);
    }

    if (this->getk() != TOK_LPAREN) {
        std::cerr << "Except '(' in Function Declaration" << std::endl;
        exit(1);
    }

    this->getk();
    while (this->getTokType() != TOK_RPAREN) {
        if (this->getTokType() != TOK_VARIABLE) {
            std::cerr << "Except VARIABLE!!!" << std::endl;
            exit(1);
        }
        args.push_back(new IdentAST(this->getTokStr()));
        this->getk();
        if (this->getTokType() == TOK_COMMA) {
            this->getk();
            continue;
        } else if (this->getTokType() == TOK_RPAREN) {
            break;
        } else {
            std::cerr << "Error Parse Func Variable List" << std::endl;
        }

    }
    std::clog << "#" << std::endl;
}



